﻿/**
<code>
  <author name="Zealic" email="zealic(at)gmail.com" type="Creator"/>
</code>
**/
using System;
using System.Text.RegularExpressions;


namespace Zealib.Template
{
    /// <summary>
    /// Regex template.
    /// </summary>
    public class RegexTemplate// : ITemplate
    {
        private const int MAX_EXP_LENGTH = 1024;
        private string _Content;

        /// <summary>
        /// Create a template.
        /// </summary>
        public RegexTemplate()
        {
            _Content = "";
        }

        public char? EscapeChar
        {
            get;
            set;
        }

        public string Prefix
        {
            get;
            set;
        }

        public string Suffix
        {
            get;
            set;
        }

        protected string EscapeExpression(string regex)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Create a template with content.
        /// </summary>
        /// <param name="content">content.</param>
        public RegexTemplate(string content)
        {
            _Content = content;
        }

        public bool Nested
        {
            get;
            set;
        }

        public string Content
        {
            get { return _Content; }
            set
            {
                if (value == null) throw new ArgumentNullException("value");
                _Content = value;
            }
        }

        /// <devdoc>
        /// Validate variable name.
        /// </devdoc>
        private bool ValidateName(string name)
        {
            const string PAT_VALID = @"^[\w_][\w\d_]{1,1024}$";
            if (null == name) throw new ArgumentNullException("name");
            return Regex.IsMatch(name, PAT_VALID);
        }

        public string Process(IVariableSource variableSource)
        {
            if (variableSource == null) throw new ArgumentNullException("variableSource");
            Func<string, string> evaluateCallback = 
                name => variableSource.Resolve(name);
            return ProcessCore(evaluateCallback);
        }

        /// <summary>
        /// Format
        /// </summary>
        protected virtual string ProcessCore(Func<string, string> callback)
        {
            // Extract variables
            const string PAT_EXP = @"(?<prefix>[\\]{1,2})?\$\{(?<name>[\w\d_]{1,1024})\}";
            const string PAT_RESERVE = @"[\\]\$\{[\w\d_]{1,1024}\}";
            bool nested = Nested;
            string result = Content;
            // Process nested
            int count = 0;
            do
            {
                count = 0;
                result = Regex.Replace(result, PAT_EXP,
                    new MatchEvaluator(
                        delegate(Match m)
                        {
                            string prefix = m.Result("${prefix}");
                            string name = m.Result("${name}");
                            if (!ValidateName(name))
                                ThrowInvalidExpression(name);
                            string content = m.Value;
                            if (prefix == @"\")
                                return content;
                            count++;
                            if (prefix == @"\\")
                                return @"\" + callback(name);
                            return callback(name);
                        }
                    )
                , RegexOptions.Singleline);
                if (!nested) break;
            } while (count != 0);
            // If completed nested, remove escape.
            result = Regex.Replace(result, PAT_RESERVE,
                new MatchEvaluator(delegate(Match m) { return m.Value.Substring(1); }));
            return result;
        }

        private void ThrowInvalidExpression(string expression)
        {
            throw new FormatException(string.Format("Invalid expression {0} 无效。", expression));
        }

        private void ThrowMaxLengthException(string expression)
        {
            throw new FormatException(string.Format("Variable {0} length over {1}.", expression, MAX_EXP_LENGTH));
        }
    }
}
